﻿using System;
using NUnit.Framework;
using ValidationFramework.Configuration;
using ValidationFramework.Reflection;
using System.Xml.Serialization;
using System.Xml;

using ValidationFramework.Specifications.Tests.Helpers;
using ValidationFramework.Specifications;
namespace ValidationFramework.Tests.Configuration.ConfigReaders
{
    [TestFixture]
    public class IsRequiredParserTests
    {
        [XmlRoot("person")]
        public class Person
        {
            [XmlAttribute("name")]
            public string Name
            {
                get;
                set;
            }

            [XmlAttribute("age")]
            public int Age
            {
                get;
                set;
            }
        }

        [Test]
        public void Should_Be_Able_To_Read_Initial_Value_From_Xml()
        {
            // Arrange
            const string validatorXml = "<rule initialValue='10' typeName='RequiredRule' ruleSet='foo' />";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;

			var descriptor = FakeRuleContext.GetContext(typeof(int));
            var parser = new IsRequiredParser();

            // Act
            var validator = (IsRequiredSpec<int>)parser.GetSpecification(validatorXmlElement, descriptor);

            // Assert
            Assert.AreEqual(10, validator.InitialValue);
        }

        [Test]
        public void Should_Infer_Spec_Type_From_Context()
        {
            // Arrange
            const string validatorXml = "<rule typeName='RequiredRule'/>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;

            var parser = new IsRequiredParser();

            // Act -- should cast without error
            var validator = (IsRequiredSpec<int>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(int)));
            var dateTimeRule = (IsRequiredSpec<DateTime>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(DateTime)));
        }

        [Test]
        public void Should_Work_For_Nullable_Types()
        {
            // Arrange
            const string validatorXml = "<rule typeName='RequiredRule'/>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;

            var configReader = new IsRequiredParser();

            // Act -- should cast without error
            var validator = (IsRequiredSpec<int?>)configReader
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(int?)));
        }

        [Test]
        public void Should_Infer_Spec_Type_Even_For_Complex_Type()
        {
            // Arrange
            const string validatorXml = "<rule typeName='RequiredRule'/>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var configReader = new IsRequiredParser();

            // Act - should cast with no error
            var validator = (IsRequiredSpec<Person>)configReader
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(Person)));
        }

        [Test]
        public void Should_Parse_Datetime_String_When_Given_As_Inital_Value()
        {
            // Arrange
            const string validatorXml = "<rule errorMessage='hello' initialValue='10 Mar 2007' typeName='RequiredRule' ruleSet='foo' />";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var parser = new IsRequiredParser();

            // Act
			var context = new RuleContext(typeof(DateTime), new FakeReflectionInfo(TypePointers.DateTimeTypeHandle, "TestProperty", DateTime.Now));
			
            var validator = (IsRequiredSpec<DateTime>)parser
				.GetSpecification(validatorXmlElement, context);

            // Assert
            Assert.AreEqual(new DateTime(2007, 3, 10), validator.InitialValue);
        }

        [Test]
        public void Should_Parse_Complex_Type_When_Used_As_Initial_Value()
        {
            // Arrange
            const string validatorXml = "<rule errorMessage='hello' typeName='RequiredRule' ruleSet='foo' ><person name='john' age='23'/></rule>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var parser = new IsRequiredParser();

            // Act
            var validator = (IsRequiredSpec<Person>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(Person)));

            // Assert
            Assert.AreEqual(23, validator.InitialValue.Age);
            Assert.AreEqual("john", validator.InitialValue.Name);
        }


        public enum Numbers
        {
            One,
            Two,
            Three
        }

        [Test]
        public void Should_Parse_Enum_When_Given_As_Initial_Value()
        {
            // Arrange
            const string validatorXml = "<rule errorMessage='hello' initialValue='Two' typeName='RequiredRule' ruleSet='foo' />";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var parser = new IsRequiredParser();

            // Act
            var validator = (IsRequiredSpec<Numbers>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(Numbers)));

            // Assert
            Assert.AreEqual(Numbers.Two, validator.InitialValue);
        }

        [Test]
        public void Should_Default_Enum_When_Given_No_Initial_Value()
        {
            // Arrange
            const string validatorXml = "<rule typeName='RequiredRule'/>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var parser = new IsRequiredParser();

            // Act
            var validator = (IsRequiredSpec<Numbers>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(Numbers)));

            // Assert
            Assert.AreEqual(Numbers.One, validator.InitialValue);
        }


        [Test]
        public void Should_Null_Initial_Value_When_Attached_To_Nullable_Type_And_Given_No_Initial_Value()
        {
            // Arrange
            const string validatorXml = "<rule typeName='RequiredRule'/>";
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(validatorXml);
            var validatorXmlElement = xmlDocument.DocumentElement;
            var parser = new IsRequiredParser();

            // Act
            var validator = (IsRequiredSpec<Numbers?>)parser
				.GetSpecification(validatorXmlElement, FakeRuleContext.GetContext(typeof(Numbers?)));

            // Assert
            Assert.IsNull(validator.InitialValue);
        }
    }
}
